import json
import re

from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.core.mail import send_mail
from django.db import IntegrityError
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData

from apps.users.models import User, Address
from celery_tasks.send_email.task import send_ad_email
from utils import regs
from utils.email_active import get_email_active_url, get_email_active_html
from utils.goods import merge_cart_from_cookie_to_redis
from utils.views import LoginRequiredJSONMixin


# 1. 添加获取用户名对应用户个数的类视图
class UsernameCountView(View):
    def get(self, request, username):
        # return HttpResponse(username)
        # 1. 去数据库中把用户名等于username的用户的数量查出来
        count = User.objects.filter(username=username).count()
        # 2. 返回如下格式的数据 {'code': 0, 'errmsg': 'ok', 'count': 0}
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'count': count})


# 2. 添加获取手机号码对应用户个数的类视图
class MobileCountView(View):
    def get(self, request, mobile):
        # 1. 去数据库中把手机号码等于mobile变量的用户的数量查出来
        count = User.objects.filter(mobile=mobile).count()
        # 2. 返回如下格式的数据 {'code': 0, 'errmsg': 'ok', 'count': 0}
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'count': count})


# 3. 添加注册的类视图
class RegisterView(View):
    def post(self, request):
        # 1. 获取参数
        print(request)
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        print(json_dict)

        username = json_dict.get('username')
        password = json_dict.get('password')
        password2 = json_dict.get('password2')
        mobile = json_dict.get('mobile')
        allow = json_dict.get('allow')
        sms_code = json_dict.get('sms_code')

        # 2. 校验参数
        # 判断参数是否齐全
        if not all([username, password, password2, mobile, allow]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必传参数!'})
        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_]{5,20}$', username):
            return JsonResponse({'code': 400, 'errmsg': 'username格式有误!'})
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({'code': 400, 'errmsg': 'password格式有误!'})
        # 判断两次密码是否一致
        if password != password2:
            return JsonResponse({'code': 400, 'errmsg': '两次输入不对!'})
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': 'mobile格式有误!'})
        # 判断是否勾选用户协议
        if allow != True:
            return JsonResponse({'code': 400, 'errmsg': 'allow格式有误!'})

        # 判断用户的短信验证码正不正确
        # 1 拿到存储的短信验证码
        conn = get_redis_connection('code')
        sms_code_redis = conn.get('mobile_{}'.format(mobile))
        # 2 如果没有了，则提示请重新获取短信验证码
        if not sms_code_redis:
            return JsonResponse({'code': 400, 'errmsg': '请先发送短信验证码'})
        # 3 如果有，把二进制转换成字符串
        sms_code_redis = sms_code_redis.decode()
        # 4 如果不相等则提示短信验证码错误
        if sms_code_redis != sms_code:
            return JsonResponse({'code': 400, 'errmsg': '短信验证码错误'})

        # 3. 插入数据
        try:
            User.objects.create_user(
                username=username, password=password, mobile=mobile)
        except IntegrityError as err:
            return JsonResponse({'code': 400, 'errmsg': str(err)})

        # 4. 返回结果
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 4. 添加登录的类视图
class LoginView(View):
    def post(self, request):
        # 1. 接收请求参数
        json_str = request.body.decode()
        req_dict = json.loads(json_str)

        username = req_dict.get('username')
        password = req_dict.get('password')
        remembered = req_dict.get('remembered')
        # print(username, password, remembered)

        # 2. 校验
        if not all([username, password]):
            return JsonResponse({'code': 400, 'errmsg': '用户名和密码不能为空'})

        # 3. 判断用户名和密码是不是正确
        # authenticate
        #     返回一个user代表用户名密码正确
        #     返回None代表 用户名或密码错误
        user = authenticate(username=username, password=password)
        if not user:
            return JsonResponse({'code': 400, 'errmsg': '用户名或密码错误'})

        # 4. 登录
        login(request, user)

        # 5. 实现记住密码的功能
        if remembered:
            # 记住密码: None 长期有效
            request.session.set_expiry(None)
        else:
            # 不记住密码
            # 设置session的有效期为 关闭浏览器时消亡
            request.session.set_expiry(0)

        # 5. 定义返回结果
        response = JsonResponse({'code': 0, 'errmsg': '登录成功！'})

        # 6. 合并购物车
        merge_cart_from_cookie_to_redis(request, user, response)

        # 7. 注册时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, 3600 * 24 * 15)
        return response


# 5. 添加退出的类试图
class LogoutView(View):
    def get(self, request):
        # 1. 清空session中登录信息
        logout(request)
        # 2. 删掉cookie中的信息
        response = JsonResponse({'code': 0, 'errmsg': '退出成功！'})
        response.delete_cookie('username')
        # 3. 返回结果
        return response


# 6. 添加获取个人信息的类视图（判断用户是否登录的类视图）
class UserInfoView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # 只要类视图继承自LoginRequiredMixin, 具体请求方式方法中的代码只有登录
        # 的情况下才会执行
        # 1. 返回用户信息
        user = request.user
        return JsonResponse({
            'code': 0,
            'errmsg': '个人中心',
            "info_data": {
                "username": user.username,
                "mobile": user.mobile,
                "email": user.email,
                "email_active": user.email_active
            }
        })


# 7. 添加设置邮箱的类视图
class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        # 1. 获取请求参数 用户填写的邮箱
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        email = json_dict.get('email')

        # 2. 校验参数
        if not email:
            return JsonResponse({'code': 400, 'errmsg': '缺少email参数'})
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return JsonResponse({'code': 400, 'errmsg': '参数email有误'})

        # 3. 修改用户的邮箱为传进来的邮箱，同时把邮箱是否激活的字段都重置为未激活 False
        user = request.user
        user.email = email
        user.email_active = False
        user.save()

        # 4. 发验证邮件
        # 4.1 生成一个激活邮箱的链接
        verify_url = get_email_active_url(request)
        # 4.2 得到激活邮件的网页模板
        html_message = get_email_active_html(email, verify_url)
        # 4.3 发送邮件
        send_mail(
            '美多商城邮箱激活', "", settings.DEFAULT_FROM_EMAIL, [user.email],
            html_message=html_message
        )
        # 5. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '修改邮箱成功'})


# 8. 添加激活邮箱的类视图
class VerifyEmailView(View):
    def put(self, request):
        # 1. 接收参数token
        token = request.GET.get('token')

        # 2. 解析token得到用户标识，如果解析不出来则响应错误的结果
        ser = Serializer(settings.SECRET_KEY)
        try:
            user_dict = ser.loads(token)
        except BadData as err:
            return JsonResponse({'code': 400, 'errmsg': str(err)})
        # print(user_dict)   # {'user_id': 7, 'email': '782555894@qq.com'}
        user_id = user_dict.get('user_id')
        email = user_dict.get('email')

        # 3. 激活用户的邮箱（把email_active从False设置为True）
        try:
            user = User.objects.get(id=user_id, email=email)
        except User.DoesNotExists as err:
            return JsonResponse({'code': 400, 'errmsg': '激活邮箱失败！'})
        user.email_active = True
        user.save()

        # 4. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '激活邮箱成功！'})


# 9. 用来添加用户收货地址的类试图
class CreateAddressView(LoginRequiredJSONMixin, View):
    def post(self, request):
        # 1. 接收参数
        json_str = request.body.decode()
        json_dict = json.loads(json_str)

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2. 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400, 'errmsg': '必传参数不能为空'})
        if not re.match(regs.MOBILE_REG, mobile):
            return JsonResponse({'code': 400, 'errmsg': 'mobile参数不合法'})
        if tel and not re.match(regs.TEL_REG, tel):
            return JsonResponse({'code': 400, 'errmsg': 'tel参数不合法'})
        if email and not email.match(regs.EMAIL_REG, email):
            return JsonResponse({'code': 400, 'errmsg': 'email参数不合法'})

        # 3. 往数据库中添加用户的收货地址数据
        print(json_dict)
        address = Address(
            user=request.user, **json_dict
        )
        address.save()

        # 4. 返回结果
        return JsonResponse({
            'code': 0,
            'errmsg': '添加地址成功',
            'address_dict': address.to_dict()
        })


# 10. 收货地址列表的接口
class AddressView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # 1. 去数据库中把当前用户的地址查出来
        user = request.user
        # addresses = Address.objects.filter(user=user)
        addresse_set = user.address_set.filter(is_deleted=False)

        # 2. 转成想要的格式
        addresses = []
        for address in addresse_set:
            addresses.append(address.to_dict())
        # 3. 返回结果
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'addresses': addresses,
            'default_address_id': user.default_address.id  if user.default_address else None
        })


# 11. 修改和删除收货地址的类视图
class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, id):
        # 1. 接收参数
        json_str = request.body.decode()
        json_dict = json.loads(json_str)

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2. 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400, 'errmsg': '必传参数不能为空'})
        if not re.match(regs.MOBILE_REG, mobile):
            return JsonResponse({'code': 400, 'errmsg': 'mobile参数不合法'})
        if tel and not re.match(regs.TEL_REG, tel):
            return JsonResponse({'code': 400, 'errmsg': 'tel参数不合法'})
        if email and not re.match(regs.EMAIL_REG, email):
            return JsonResponse({'code': 400, 'errmsg': 'email参数不合法'})

        # 3. 利用id从数据库查出要修改的地址
        try:
            # 为了防止安全漏洞，确保修改的地址就是当前用户的
            # 需要额外添加  user=request.user的参数以确保安全
            address = Address.objects.get(id=id, user=request.user)
        except Address.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': '收货地址不存在'})

        # 4. 对地址做修改
        address.receiver = receiver
        address.province_id = province_id
        address.city_id = city_id
        address.district_id = district_id
        address.place = place
        address.mobile = mobile
        address.tel = tel
        address.email = email
        address.save()

        # 5. 返回结果
        return JsonResponse({
            'code': '0', 'errmsg': '修改成功！',
            'addresse_dict': address.to_dict()
        })

# 12. 删除收货地址的类视图
class DestroyAddressView(LoginRequiredJSONMixin, View):
    def get(self, request, id):
        # 1. 利用id查出要删除的地址
        try:
            address = Address.objects.get(id=id, user=request.user)
        except Address.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': '地址未找到'})
        # 2. 把地址的is_deleted设置为True
        address.is_deleted = True
        address.save()
        # 3. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '删除成功'})


#13. 设置默认地址的类试图
class DefaultAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, id):
        user = request.user
        # 1. 利用id查出地址
        try:
            address = Address.objects.get(id=id, user=user)
        except Address.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': '地址不存在！'})

        # 2. 把该地址设置为用户的默认地址
        user.default_address = address
        user.save()

        # 3. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '设置默认地址成功！'})


# 14. 修改收货地址标题的类视图
class UpdateTitleAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, id):
        # 1. 接收参数title
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        title = json_dict.get('title')

        # 2. 校验参数
        if not title:
            return JsonResponse({'code': 400, 'errmsg': 'tilte参数不能为空！'})

        # 3. 利用id查出要修改的收货地址
        try:
            address = Address.objects.get(id=id, user=request.user)
        except Address.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': '要修改的说活地址不存在'})

        # 4. 修改收货地址的标题
        address.title = title
        address.save()

        # 5. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '修改地址标题成功！'})


# 15. 修改密码的类视图
class PasswordView(LoginRequiredJSONMixin, View):
    def put(self, request):
        user = request.user
        # 1. 接收参数
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        old_password = json_dict.get('old_password')
        new_password = json_dict.get('new_password')
        new_password2 = json_dict.get('new_password2')

        # 2. 校验参数
        if not all([old_password, new_password, new_password2]):
            return JsonResponse({'code': 0, 'errmsg': '参数不能为空！'})

        if not re.match(regs.PASSWORD_REG, new_password):
            return JsonResponse({'code': 0, 'errmsg': '密码不符合规则！'})

        if new_password != new_password2:
            return JsonResponse({'code': 0, 'errmsg': '确认密码和密码不一致！'})

        # 3. 判断用户的旧密码是否正确
        #   user.check_password(待校验的密码)
        #    返回true，代表待校验的密码是正确的
        #    返回false，代表待校验的密码是错误的
        if not user.check_password(old_password):
            return JsonResponse({'code': 0, 'errmsg': '旧密码错误！'})

        # 4. 修改用户的密码为新密码
        user.set_password(new_password)
        user.save()

        # 5. 退出登录
        # 5.1  清空session中登录信息
        logout(request)
        # 5.2 删掉cookie中的信息
        response = JsonResponse({'code': 0, 'errmsg': '修改密码成功！'})
        response.delete_cookie('username')

        # 6. 返回结果
        return response


# 16. 发送广告邮件的类视图
class SendAdEmailsView(LoginRequiredJSONMixin, View):
    def post(self, request):
        # 1. 判断是不是超级管理员
        if not request.user.is_superuser:
            return JsonResponse({'code': 400, 'errmsg': '当前用户非超级管理员'})

        # 2. 接收参数
        json_str = request.body.decode()
        json_list = json.loads(json_str)
        # print(json_list)  # ['782555894@qq.com', '709129330@qq.com']

        # 3. 发送邮件
        for email in json_list:
            send_ad_email.delay(email)

        # 4. 返回结果
        return JsonResponse({'code': 0, 'errmsg': '发送广告邮件成功'})


# 20. 添加使用cookie登录的视图函数
def login_cookie(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        username = request.POST.get('username')
        response = HttpResponse('登录成功')
        # 往cookie写入用户名，记录当前登录的是谁
        response.set_cookie('username', username)
        return response


# 6. 添加使用cookie登录成功的视图函数
def login_cookie_success(request):
    username = request.COOKIES.get('username')
    print(username)
    return HttpResponse('{}个人中心'.format(username))


# 7. 添加使用session登录的视图函数
def login_session(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        username = request.POST.get('username')
        response = HttpResponse('登录成功')
        # 往session中写入用户名，记录当前登录的是谁
        request.session['username'] = username
        return response


# 8. 个人中心页面
def login_session_success(request):
    print(request)
    username = request.session.get('username')
    return HttpResponse('{}个人中心'.format(username))
